package main

import (
	"fmt"
	"io"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"
)

type Server struct {
	Ip           string
	Port         int
	OnlineMap    map[string]*User // 注意对象都是带*的  方便调用方法
	mapLock      sync.Mutex       // 给map加上同步功能
	BroadCastBus chan string      // 消息总线
}

func NewServer(ip string, port int) *Server {
	// 返回的是指针 方便修改内部参数
	server := &Server{
		Ip:           ip,
		Port:         port,
		OnlineMap:    make(map[string]*User),
		BroadCastBus: make(chan string),
	}
	return server
}

func (s *Server) Start() {
	listen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Ip, s.Port))
	if err != nil {
		fmt.Println("net listen err: ", err)
		return
	}
	defer listen.Close()

	go s.StartBroadCastBusListener()

	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("net accept err: ", err)
			continue
		}
		go s.onConnected(conn)
	}
}

// 转移消息到user管道
func (s *Server) StartBroadCastBusListener() {
	for {
		msg := <-s.BroadCastBus
		split := strings.Split(msg, "@@!!")
		fromUser := split[0]
		msg = split[1]
		s.mapLock.Lock()
		for _, user := range s.OnlineMap {
			if user.Name != fromUser {
				user.C <- msg
			}
		}
		s.mapLock.Unlock()
	}
}

// 广播消息
// 其实不应该要广播总线 用户少的时候单独map循环发就够了
func (s *Server) broadCast(user *User, msg string) {
	msg = user.Name + "@@!!" + msg
	s.BroadCastBus <- msg
}

// 处理每次连接请求
func (s *Server) onConnected(conn net.Conn) {
	user := NewUser(conn, s)
	user.Online()
	// 监听客户端发来的消息
	go s.clientMsgListener(conn, user)

	for {
		select {
		// 用户活跃  重置定时器 当然这里什么也不用做 这里会遍历所有条件
		case <-user.IsLive:
			fmt.Println("accept true")
		// 阻塞当前handler  进行超时踢出下线
		// 核心要点
		// 每次case完成重新进行新一轮select时 会执行所有的case条件
		// 然后等待任何一个case条件满足时执行对应的case下的语句
		// 这里可以用来每次进行重新设置10秒钟
		case <-time.After(time.Second * 600):
			// 超时了 执行踢人方法
			user.ForceOffline()
			conn.Close()
			// 退出goroutine
			runtime.Goexit()
		}
	}

}

// 监听客户端发来的消息
func (s *Server) clientMsgListener(conn net.Conn, user *User) {
	buf := make([]byte, 4096)
	for {
		n, err := conn.Read(buf)
		// 客户端断开连接时 收到0长度的包
		if n == 0 {
			user.Offline()
			return
		}
		if err != nil && err != io.EOF {
			fmt.Println("conn read err ", err)
			return
		}
		msg := string(buf[:n-1]) // 去掉结尾的\n
		user.HandleMessage(msg)
		// 防止并发情况下管道被关闭
		if user != nil && user.IsLive != nil {
			user.IsLive <- true
		}
	}
}
